/**
 * (C) 2007 - Boston University
 * 	Contact: Michael Ocean 

  This work is a part of the larger snBench project.  Information
  can be found at: http://csr.bu.edu/snbench

  This program is free software; you can redistribute it and/or
  modify it under the terms of Version 2 of the GNU General Public 
  License.
  
  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  General Public License for more details.
  
  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free
  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**/
package snobject;

/**
 * An snStruct is a collection of keys and values where keys
 * must be strings (for sanity's sake).  Unclear how useful
 * this will be in general or if we want to allow it 
 * at all but it has clear use for some types of data
 * 
 * looks like a series of 0 or more entries which look like:
 * <entry>
 * 	<key> <!-- snstring --> </key>
 *  <value> <!-- snobject --> </value>
 * <entry>
 * 
 * could have used a series of pairs, but the XML is easier
 * to follow this way.
 */

import snobject.snObject;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Set;

import step.parse.SyntaxError;

public class snStruct extends snObject {
	
	public snStruct() {
		super();
		
		content = new Hashtable<String, snObject>();
	}

	@SuppressWarnings("unchecked")
	private Hashtable<String, snObject> castContent(){
		return ((Hashtable<String, snObject>)content);
	}
	
	public void clear(){
		castContent().clear();
	}
	
	public int size(){
		return castContent().size();
	}
	
	public boolean hasKey(snString snKey){
		return castContent().containsKey(snKey.getString());
	}
		
	public void put(snString snKey, snObject snVal){
		castContent().put(snKey.getString(), snVal);
	}

	public void put(String Key, String Val){
		//System.err.println("put:" + Key + "," + Val); 
		castContent().put(Key, new snString(Val));
	}
	
	public void putAll(Hashtable<String,String> ht){
		String sk = null;
		Enumeration<String> e = ht.keys();
		
		while(e.hasMoreElements()){
			sk = e.nextElement();
			castContent().put(sk, new snString(ht.get(sk)));	
		}
	}

	public void putAllSnObjects(Hashtable<String,snObject> ht){
		String sk = null;
		Enumeration<String> e = ht.keys();
		
		while(e.hasMoreElements()){
			sk = e.nextElement();
			castContent().put(sk, ht.get(sk));	
		}
	}

	
	public snObject get(String sKey){
		return castContent().get(sKey);
	}
	
	public snObject get(snString snKey){
		return castContent().get(snKey.getString());
	}
	
	public snObject remove(String sKey){
		return castContent().remove(sKey);
	}
	
	public snObject remove(snString snKey){
		return castContent().remove(snKey.getString());
	}
	
	public String getXmlMimeType() { return MimeTypes.STRUCT; }
	public String getWebMimeType() { return "text/xml"; }
	public String getBinaryMimeType() { return "text/plain"; }
	
	
	public Set<String> getKeys(){
		return castContent().keySet();
	}
	
	public Collection<snObject> getValues(){
		return castContent().values();
	}
	
	public void xmlWrite(java.io.OutputStream out, int depth) throws java.io.IOException {
		xmlWritePre(out,depth);
		
		for(String o:getKeys()){			
			step.Node.Indent(out, depth);
			out.write("<entry>".getBytes());
			out.write("<key>\n".getBytes());
			
			snString s = new snString(o);
			s.xmlWrite(out,depth+1);
			
			step.Node.Indent(out, depth);
			out.write("</key>".getBytes());
			out.write("<value>\n".getBytes());
				
			castContent().get(o).xmlWrite(out,depth+1);

			step.Node.Indent(out, depth);
			out.write("</value>".getBytes());
			out.write("</entry>\n".getBytes());
		}
		xmlWritePost(out,depth);
	}
	
	public void webWrite(java.io.OutputStream out, int depth) throws java.io.IOException {			
		xmlWritePre(out,depth);
		
		for(String o:getKeys()){
			step.Node.Indent(out, depth);
			out.write("<entry>".getBytes());
			out.write("<key>\n".getBytes());
			
			snString s = new snString(o);
			s.xmlWrite(out,depth+1);
			
			out.write("</key>".getBytes());
			out.write("<value>\n".getBytes());
				
			castContent().get(o).xmlWrite(out,depth+1);

			step.Node.Indent(out, depth);
			out.write("</value>".getBytes());
			out.write("</entry>\n".getBytes());
		}
		
		xmlWritePost(out,depth);
		
	}

	public void binaryWrite(java.io.OutputStream out) throws java.io.IOException {
		xmlWrite(out,0);
	}
		
	public static snStruct xmlParse(org.w3c.dom.Element e) 
	throws SyntaxError, MimeException {
		snString snKey = null;
		snObject snVal = null;
		Node c = null;
		Node n = null;
		NodeList cl = null;
		NodeList nl = null;
		snStruct snstruct = new snStruct();
		
		if (!e.getTagName().equals("snobject"))
			return null;
		
		nl = e.getChildNodes();
		
		for(int i = 0; i < nl.getLength(); i++){
			n = nl.item(i);
			if (n.getNodeName().equalsIgnoreCase("entry")){
				cl = n.getChildNodes();
				snKey = null;
				snVal = null;
				for(int j = 0; j < cl.getLength(); j ++ ){
					c = cl.item(j);
					
					
					if (c.getNodeName().equalsIgnoreCase("key")){
						/* now we have to get the snObject inside this element */
						snKey = snString.xmlParse(getChildSNObject(c));
					}
					if (c.getNodeName().equalsIgnoreCase("value")){
						snVal = snObject.xmlParse(getChildSNObject(c));
					}
					else if(n.getNodeType() == org.w3c.dom.Node.TEXT_NODE) {
						/* ignore white space */
					}
				}
				if((snKey==null)&&(snVal==null)){
					/* broken entry with no harm -- skip */
				}
				else if(snKey==null){
					System.err.println("Key is null");
					throw new SyntaxError("Parsing failed for entry");
				}
				else if (snVal==null){
					System.err.println("Value is null");
					throw new SyntaxError("Parsing failed for entry");
				}
				else{
					snstruct.put(snKey, snVal);
				}

			}
			else if(n.getNodeType() == org.w3c.dom.Node.TEXT_NODE) {
				/* ignore white space */
			}
		}
		return snstruct;
	}

}
